package com.liujit.gateway.filter;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.liujit.common.constants.AuthConstant;
import com.liujit.common.constants.HeaderConstant;
import com.liujit.gateway.constants.FilterOrderConstant;
import com.liujit.gateway.constants.GateWayConstant;
import com.liujit.gateway.domain.SysLog;
import com.liujit.gateway.utils.IpUtil;
import com.liujit.gateway.utils.LogUtil;
import io.netty.buffer.UnpooledByteBufAllocator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Date;
import java.util.Objects;

/**
 * @Description: 请求日志打印
 * @Author: liujun
 * @Date: 2021/6/30 6:36 下午
 */
@Slf4j
@Component
public class RequestLogFilter implements GlobalFilter, Ordered {

    @Override
    public int getOrder() {
        return FilterOrderConstant.REQUEST_LOG_FILTER;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 白名单
        if (exchange.getAttribute(GateWayConstant.IGNORE_URL)) {
            return chain.filter(exchange);
        }
        ServerHttpRequest request = exchange.getRequest();
        // 创建日志对象
        SysLog sysLog = this.create(request);
        HttpHeaders headers = request.getHeaders();
        MediaType mediaType = headers.getContentType();
        if (Objects.nonNull(mediaType) && LogUtil.isUploadFile(mediaType)) {
            sysLog.setReqParam("上传文件");
        } else {
            if (HttpMethod.GET.equals(request.getMethod())) {
                String uriQuery = request.getURI().getQuery();
                if (StrUtil.isNotBlank(uriQuery)) {
                    sysLog.setReqParam(request.getURI().getQuery());
                }
            } else {
                long contentLength = headers.getContentLength();
                if (contentLength > 0) {
                    ServerRequest serverRequest = ServerRequest
                            .create(exchange, HandlerStrategies.withDefaults().messageReaders());
                    return serverRequest.bodyToMono(String.class).flatMap(reqBody -> {
                        sysLog.setReqParam(JSONUtil.toJsonStr(JSONUtil.parseObj(reqBody)));
                        exchange.getAttributes().put(GateWayConstant.SYS_LOG, sysLog);
                        // 重写原始请求
                        ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {

                            @Override
                            public HttpHeaders getHeaders() {
                                HttpHeaders httpHeaders = new HttpHeaders();
                                httpHeaders.putAll(super.getHeaders());
                                return httpHeaders;
                            }

                            @Override
                            public Flux<DataBuffer> getBody() {
                                NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(
                                        new UnpooledByteBufAllocator(false));
                                DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(reqBody.getBytes());
                                return Flux.just(bodyDataBuffer);
                            }
                        };
                        return chain.filter(exchange.mutate().request(decorator).build());
                    });
                }
            }
        }
        exchange.getAttributes().put(GateWayConstant.SYS_LOG, sysLog);
        return chain.filter(exchange);
    }

    /**
     * 创建日志对象
     *
     * @return
     */
    private SysLog create(ServerHttpRequest request) {
        HttpHeaders httpHeaders = request.getHeaders();
        // 当前请求路径
        String currentUrl = request.getPath().pathWithinApplication().value();
        JSONObject payload = JSONUtil.parseObj(httpHeaders.getFirst(HeaderConstant.PAYLOAD));
        // 创建日志对象
        final SysLog sysLog = new SysLog();
        sysLog.setClientId(payload.getStr(AuthConstant.DETAILS_CLIENT_ID));
        sysLog.setServiceId(currentUrl.substring(1).split("/")[0]);
        sysLog.setUserId(payload.getLong(AuthConstant.DETAILS_USER_ID));
        sysLog.setAccount(payload.getStr(AuthConstant.DETAILS_USER_NAME));
        sysLog.setMenuId(Convert.toLong(httpHeaders.getFirst(HeaderConstant.MENU_ID)));
        sysLog.setVisitCode(httpHeaders.getFirst(HeaderConstant.VISIT_CODE));
        // ip地址
        String ip = IpUtil.getIp(request.getRemoteAddress(), httpHeaders);
        sysLog.setIp(ip);
        sysLog.setLocation(IpUtil.getCityInfo(ip));
        // 浏览器信息
        UserAgent userAgent = UserAgentUtil.parse(httpHeaders.getFirst(HeaderConstant.USER_AGENT));
        sysLog.setBrowser(userAgent.getBrowser().getName());
        sysLog.setOs(userAgent.getOs().getName());
        sysLog.setReqUrl(currentUrl);
        sysLog.setReqMethod(request.getMethodValue());
        sysLog.setReqTime(new Date());
        return sysLog;
    }
}
